/*session管理模块*/

#pragma once

#include "util.hpp"
#include "log.hpp"

using namespace ns_util;
typedef enum
{
    UNLOGIN,
    LOGIN
} ss_statu;

class session
{
private:
    uint64_t _ssid;            // 标识符
    uint64_t _uid;             // session对应的用户ID
    ss_statu _statu;           // 用户状态：未登录，已登录
    wsserver_t::timer_ptr _tp; // session关联的定时器

public:
    session(uint64_t ssid)
        : _ssid(ssid)
    {
        LOG(DEBUG) << "SESSION" << this << "被创建！！\n";
    }
    ~session()
    {
        LOG(DEBUG) << "SESSION" << this << "被释放！！\n";
    }

public:
    uint64_t ssid() { return _ssid; }
    void set_statu(ss_statu statu) { _statu = statu; }
    void set_user(uint64_t uid) { _uid = uid; }
    uint64_t get_user() { return _uid; }
    bool is_login() { return _statu == LOGIN; }
    void set_timer(const wsserver_t::timer_ptr &tp) { _tp = tp; }
    wsserver_t::timer_ptr &get_timer() { return _tp; }
};

#define SESSION_TIMEOUT 30000
#define SESSION_FOROVER -1

using session_ptr = std::shared_ptr<session>;

class session_manager
{
private:
    uint64_t _next_ssid; // session id
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _sessions; //<ssid,session>
    wsserver_t *_server;

public:
    session_manager(wsserver_t *server)
        : _next_ssid(1),
          _server(server)
    {
        LOG(DEBUG) << "session管理器初始化完毕!\n";
    }
    ~session_manager()
    {
        LOG(DEBUG) << "session管理器即将销毁!\n";
    }

public:
    // 创建session
    session_ptr create_session(uint64_t uid, ss_statu statu)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_next_ssid));
        ssp->set_statu(statu);
        ssp->set_user(uid);
        _sessions.insert(std::make_pair(_next_ssid, ssp));
        _next_ssid++;
        return ssp;
    }
    // 添加session
    void append_session(const session_ptr &ssp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.insert(std::make_pair(ssp->ssid(), ssp));
    }
    // 通过ssid获取session
    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _sessions.find(ssid);
        if (it == _sessions.end())
        {
            return session_ptr();
        }
        return it->second;
    }
    // 移除session
    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.erase(ssid);
    }
    // 设置定时任务
    void set_session_expire_time(uint64_t ssid, int ms)
    {
        // 依赖于websocketpp的定时器来完成session生命周期的管理。
        //  登录之后，创建session，session需要在指定时间无通信后删除
        //  但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
        //  等到退出游戏大厅，或者游戏房间，这个session应该被重新设置为临时，在长时间无通信后被删除
        session_ptr ssp = get_session_by_ssid(ssid);
        if(ssp.get() == nullptr)
        {
            return;
        }
        wsserver_t::timer_ptr tp = ssp->get_timer();
        if(tp.get() == nullptr && ms == SESSION_FOROVER)
        {
            //没有定时销毁任务，同时要设置的是永久存在的session
            return;
        }
        else if(tp.get() == nullptr && ms != SESSION_TIMEOUT)
        {
            //本来没有定时销毁任务，现在要进行设置
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,std::bind(&session_manager::remove_session,this,ssid));
            ssp->set_timer(tmp_tp);
        }
        else if(tp.get() != nullptr && ms == SESSION_FOROVER)
        {
            //本来有定时销毁任务，现在要把session设置成永久的
            // 删除定时任务--- stready_timer删除定时任务会导致任务直接被执行
            tp->cancel();   //这里还是会执行销毁session，所以后面得把这个session添加回去
            //因此重新给session管理器中，添加一个session信息, 且添加的时候需要使用定时器，而不是立即添加
            ssp->set_timer(wsserver_t::timer_ptr());    //将session关联的定时器设置为空
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));
        }
        else if(tp.get() != nullptr && ms != SESSION_FOROVER)
        {
            //本来就有定时销毁任务，现在需要更改定时时长
            //删除原来的定时任务，重新进行设置
            tp->cancel();
            ssp->set_timer(wsserver_t::timer_ptr());    //将session关联的定时器设置为空
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));

            //重新给session添加定时销毁任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,std::bind(&session_manager::remove_session,this,ssid));
            //重新设置session关联的定时器
            ssp->set_timer(tmp_tp);
        }
    }
};